<template>
  <div class="material-lab">
    <!-- 控制面板 -->
    <div class="controls">
      <el-form label-width="80px">
        <el-form-item label="几何体">
          <el-select v-model="shape" @change="updateGeometry">
            <el-option label="立方体 Box" value="box" />
            <el-option label="球体 Sphere" value="sphere" />
            <el-option label="圆环 Torus" value="torus" />
            <el-option label="圆锥 Cone" value="cone" />
          </el-select>
        </el-form-item>

        <el-form-item label="材质">
          <el-select v-model="materialType" @change="updateMaterial">
            <el-option label="Basic 材质" value="basic" />
            <el-option label="Standard 材质" value="standard" />
            <el-option label="Phong 材质" value="phong" />
          </el-select>
        </el-form-item>

        <el-form-item label="线框模式">
          <el-switch v-model="wireframe" @change="updateMaterial" />
        </el-form-item>

        <el-form-item label="颜色">
          <el-color-picker v-model="materialColor" @change="updateMaterial" />
        </el-form-item>

        <template v-if="materialType === 'standard'">
          <el-form-item label="金属度">
            <el-slider
              v-model="metalness"
              :step="0.01"
              :min="0"
              :max="1"
              @input="updateMaterial"
            />
          </el-form-item>
          <el-form-item label="粗糙度">
            <el-slider
              v-model="roughness"
              :step="0.01"
              :min="0"
              :max="1"
              @input="updateMaterial"
            />
          </el-form-item>
        </template>

        <el-form-item label="加载模型">
          <el-upload
            :show-file-list="false"
            :http-request="customUpload"
            accept=".glb"
          >
            <el-button type="primary">导入 GLB 模型</el-button>
          </el-upload>
        </el-form-item>
      </el-form>

      <el-card v-if="selectedInfo" class="info-panel">
        <div>名称：{{ selectedInfo.name || "无名" }}</div>
        <div>类型：{{ selectedInfo.type }}</div>
        <div>材质：{{ selectedInfo.materialName }}</div>
      </el-card>
    </div>

    <!-- Three.js 区域 -->
    <div
      ref="threeContainer"
      class="canvas-container"
      @click="handlePick"
    ></div>
  </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { ElMessage } from "element-plus";
import axios from "axios";

const threeContainer = ref(null);
let scene, camera, renderer, controls, mesh;
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let currentHighlighted = null;
let originalMaterial = null;

const selectedInfo = ref(null);
const shape = ref("box");
const materialType = ref("standard");
const wireframe = ref(false);
const materialColor = ref("#00aaff");
const metalness = ref(0.4);
const roughness = ref(0.6);

onMounted(() => {
  initScene();
  updateGeometry();
  animate();
});

function initScene() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
  camera.position.set(0, 1.5, 4);
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(600, 500);
  threeContainer.value.appendChild(renderer.domElement);
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  scene.add(new THREE.AxesHelper(5));
  const ambient = new THREE.AmbientLight(0x404040);
  const directional = new THREE.DirectionalLight(0xffffff, 1);
  directional.position.set(2, 4, 3);
  scene.add(ambient, directional);
}

function updateGeometry() {
  const geometryMap = {
    box: new THREE.BoxGeometry(),
    sphere: new THREE.SphereGeometry(0.75, 32, 32),
    torus: new THREE.TorusGeometry(0.6, 0.2, 16, 100),
    cone: new THREE.ConeGeometry(0.6, 1.2, 32),
  };
  const geometry = geometryMap[shape.value];
  if (mesh) {
    scene.remove(mesh);
    mesh.geometry.dispose();
  }
  mesh = new THREE.Mesh(geometry, createMaterial());
  scene.add(mesh);
}

function updateMaterial() {
  if (!mesh) return;
  mesh.material.dispose();
  mesh.material = createMaterial();
}

function createMaterial() {
  const base = {
    color: new THREE.Color(materialColor.value),
    wireframe: wireframe.value,
  };
  switch (materialType.value) {
    case "basic":
      return new THREE.MeshBasicMaterial(base);
    case "standard":
      return new THREE.MeshStandardMaterial({
        ...base,
        metalness: metalness.value,
        roughness: roughness.value,
      });
    case "phong":
      return new THREE.MeshPhongMaterial({ ...base, shininess: 100 });
  }
}

function animate() {
  requestAnimationFrame(animate);
  if (mesh) {
    mesh.rotation.x += 0.01;
    mesh.rotation.y += 0.01;
  }
  controls.update();
  renderer.render(scene, camera);
}

function handlePick(event) {
  const rect = renderer.domElement.getBoundingClientRect();
  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0) {
    const target = intersects[0].object;
    if (currentHighlighted && originalMaterial) {
      currentHighlighted.material = originalMaterial;
    }
    originalMaterial = target.material;
    currentHighlighted = target;
    target.material = target.material.clone();
    target.material.color.set(0xff6600);
    selectedInfo.value = {
      name: target.name,
      type: target.type,
      materialName: target.material.name || "默认材质",
    };
  }
}

async function customUpload({ file }) {
  const form = new FormData();
  form.append("file", file);
  try {
    const res = await axios.post(
      "http://localhost:9999/api/upload-image",
      form
    );
    loadGLB(`${res.data.url}`);
    ElMessage.success("模型上传成功");
  } catch (err) {
    console.log(err);
    ElMessage.error(err);
  }
}

function loadGLB(url) {
  const loader = new GLTFLoader();
  loader.load(url, (gltf) => {
    if (mesh) {
      scene.remove(mesh);
    }
    mesh = gltf.scene;
    scene.add(mesh);
    controls.reset();
  });
}
</script>

<style scoped>
.material-lab {
  display: flex;
  padding: 20px;
}
.controls {
  width: 260px;
  margin-right: 20px;
}
.canvas-container {
  flex: 1;
  border: 1px solid #ccc;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.info-panel {
  margin-top: 16px;
}
</style>
